/*
 * Copyright (C) 2018 Zhejiang xiaominfo Technology CO.,LTD.
 * All rights reserved.
 * Official Web Site: http://www.xiaominfo.com.
 * Developer Web Site: http://open.xiaominfo.com.
 */

package com.github.xiaoymin.map.request.amap;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.github.xiaoymin.map.common.MapClientConstants;
import com.github.xiaoymin.map.exception.MapClientException;
import com.github.xiaoymin.map.model.AmapKey;
import com.github.xiaoymin.map.model.MapLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 逆地理编码,see:https://lbs.amap.com/api/webservice/guide/api/georegeo
 * @author <a href="mailto:xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
 * 2020/09/21 14:09
 * @since:map-client 1.0
 */
public class AmapRegeoRequest {
    Logger logger= LoggerFactory.getLogger(AmapRegeoRequest.class);

    /**
     * 经纬度坐标
     * 传入内容规则：经度在前，纬度在后，经纬度间以“,”分割，经纬度小数点后不要超过 6 位。
     * 如果需要解析多个经纬度的话，请用"|"进行间隔，并且将 batch 参数设置为 true，最多支持传入 20 对坐标点。每对点坐标之间用"|"分割。
     */
    private List<MapLocation> mapLocations=new ArrayList<>();

    /**
     * 以下内容需要 extensions 参数为 all 时才生效。
     *
     * 逆地理编码在进行坐标解析之后不仅可以返回地址描述，也可以返回经纬度附近符合限定要求的POI内容（在 extensions 字段值为 all 时才会返回POI内容）。
     * 设置 POI 类型参数相当于为上述操作限定要求。参数仅支持传入POI TYPECODE，可以传入多个POI TYPECODE，相互之间用“|”分隔。
     * 该参数在 batch 取值为 true 时不生效。获取 POI TYPECODE 可以参考POI分类码表
     */
    private String poitype;
    /**
     * 搜索半径
     * radius取值范围在0~3000，默认是1000。单位：米
     */
    private int radius=1000;

    /**
     * 返回结果控制
     * extensions 参数默认取值是 base，也就是返回基本地址信息；
     * extensions 参数取值为 all 时会返回基本地址信息、附近 POI 内容、道路信息以及道路交叉口信息。
     */
    private AmapExtension extensions= AmapExtension.base;
    /**
     * 批量查询控制
     * batch 参数设置为 true 时进行批量查询操作，最多支持 20 个经纬度点进行批量地址查询操作。
     * batch 参数设置为 false 时进行单点查询，此时即使传入多个经纬度也只返回第一个经纬度的地址解析查询结果。
     */
    private Boolean batch=Boolean.FALSE;

    /**
     * 道路等级
     * 以下内容需要 extensions 参数为 all 时才生效。
     * 当roadlevel=0时，显示所有道路
     * 当roadlevel=1时，过滤非主干道路，仅输出主干道路数据
     */
    private int roadlevel;
    /**
     * 是否优化POI返回顺序
     * 以下内容需要 extensions 参数为 all 时才生效。
     * homeorcorp 参数的设置可以影响召回 POI 内容的排序策略，目前提供三个可选参数：
     * 0：不对召回的排序策略进行干扰。
     * 1：综合大数据分析将居家相关的 POI 内容优先返回，即优化返回结果中 pois 字段的poi顺序。
     * 2：综合大数据分析将公司相关的 POI 内容优先返回，即优化返回结果中 pois 字段的poi顺序。
     */
    private int homeorcorp=0;

    public static class Builder{
        private List<MapLocation> mapLocations=new ArrayList<>();
        private List<String> poitypes;
        private int radius=1000;
        private AmapExtension extensions= AmapExtension.base;
        private Boolean batch=Boolean.FALSE;
        private int roadlevel=0;
        private int homeorcorp=0;
        /**
         * 添加地理位置
         * @param mapLocation 经纬度坐标对象
         * @return 当前Builder对象
         */
        public Builder addLocation(MapLocation mapLocation){
            mapLocations.add(mapLocation);
            return this;
        }

        /**
         * 添加地理位置
         * @param lng 经度
         * @param lat 纬度
         * @return 当前Builder对象
         */
        public Builder addLocation(String lng,String lat){
            mapLocations.add(new MapLocation(lng,lat));
            return this;
        }

        /**
         * 以下内容需要 extensions 参数为 all 时才生效。
         * 逆地理编码在进行坐标解析之后不仅可以返回地址描述，也可以返回经纬度附近符合限定要求的POI内容（在 extensions 字段值为 all 时才会返回POI内容）。
         * 设置 POI 类型参数相当于为上述操作限定要求。参数仅支持传入POI TYPECODE，可以传入多个POI TYPECODE，相互之间用“|”分隔。
         * 该参数在 batch 取值为 true 时不生效。获取 POI TYPECODE 可以参考POI分类码表
         *
         * @param poitype 返回附近POI类型
         * @return 当前Builder对象
         */
        public Builder poitype(String poitype){
            if (StrUtil.isNotBlank(poitype)){
                throw new MapClientException("poitype类型不能为空");
            }
            this.poitypes.add(poitype);
            return this;
        }

        /**
         * 设置搜索半径,范围0-3000
         * @param radius 搜索半径
         * @return 当前Builder对象
         */
        public Builder redius(int radius){
            if (radius<0||radius>3000){
                throw new MapClientException("搜索半径范围在0-3000");
            }
            this.radius=radius;
            return this;
        }

        /**
         * extensions 参数默认取值是 base，也就是返回基本地址信息；
         * extensions 参数取值为 all 时会返回基本地址信息、附近 POI 内容、道路信息以及道路交叉口信息。
         * @param amapExtension 返回结果控制
         * @return 当前Builder对象
         */
        public Builder extension(AmapExtension amapExtension){
            this.extensions=amapExtension;
            return this;
        }

        /**
         * 批量查询控制
         * @param batch 批量查询控制
         * @return 当前Builder对象
         */
        public Builder batch(boolean batch){
            this.batch=batch;
            return this;
        }

        /**
         * 以下内容需要 extensions 参数为 all 时才生效。
         * 当roadlevel=0时，显示所有道路
         * 当roadlevel=1时，过滤非主干道路，仅输出主干道路数据
         * @param roadlevel 道路等级(0或1)
         * @return 当前Builder对象
         */
        public Builder roadlevel(int roadlevel){
            this.roadlevel=roadlevel;
            return this;
        }

        /**
         * 以下内容需要 extensions 参数为 all 时才生效。
         * homeorcorp 参数的设置可以影响召回 POI 内容的排序策略，目前提供三个可选参数：
         * 0：不对召回的排序策略进行干扰。
         * 1：综合大数据分析将居家相关的 POI 内容优先返回，即优化返回结果中 pois 字段的poi顺序。
         * 2：综合大数据分析将公司相关的 POI 内容优先返回，即优化返回结果中 pois 字段的poi顺序。
         * @param homeorcorp 是否优化POI返回顺序
         * @return 当前Builder对象
         */
        public Builder homeorcorp(int homeorcorp){
            this.homeorcorp=homeorcorp;
            return this;
        }

        /**
         * 构造逆址编码请求对象
         * @return 逆址编码请求对象
         */
        public AmapRegeoRequest build(){
            String poiTp=null;
            if (CollectionUtil.isNotEmpty(poitypes)){
                poiTp=CollectionUtil.join(poitypes, MapClientConstants.AMAP_JOIN_ELEMENT_CHAR);
            }
            return new AmapRegeoRequest(mapLocations,poiTp,radius,extensions,batch,roadlevel,homeorcorp);
        }


    }

    /**
     * 构建逆地理编码请求对象
     * @param mapLocations 经纬度坐标
     * @param poitype 返回附近POI类型
     * @param radius 搜索半径
     * @param extensions 返回结果控制
     * @param batch 批量查询控制
     * @param roadlevel 道路等级
     * @param homeorcorp 是否优化POI返回顺序
     */
    public AmapRegeoRequest(List<MapLocation> mapLocations, String poitype, int radius, AmapExtension extensions, Boolean batch, int roadlevel, int homeorcorp) {
        //地理位置必传,判断
        if (CollectionUtil.isEmpty(mapLocations)){
            throw new MapClientException("经纬度坐标位置不能为空");
        }
        if (mapLocations.size()>20){
            throw new MapClientException("经纬度坐标数量最大不能超过20");
        }
        this.mapLocations = mapLocations;
        this.poitype = poitype;
        this.radius = radius;
        this.extensions = extensions;
        this.batch = batch;
        this.roadlevel = roadlevel;
        this.homeorcorp = homeorcorp;
    }

    /**
     * 获取当前对象的map参数
     * @return 参数map
     */
    public Map<String,String> toMap(){
        Map<String,String> parameterMap=new HashMap<>();
        //parameterMap.put("key",amapKey.getKey());
        if (CollectionUtil.isNotEmpty(mapLocations)){
            String locations=CollectionUtil.join(mapLocations.stream().map(mapLocation -> mapLocation.getLng()+","+mapLocation.getLat()).collect(Collectors.toList()),MapClientConstants.AMAP_JOIN_ELEMENT_CHAR);
            parameterMap.put("location",locations);
        }
        parameterMap.put("extensions",extensions.name());
        parameterMap.put("radius",String.valueOf(radius));
        parameterMap.put("batch",String.valueOf(batch));
        if (extensions==AmapExtension.all){
            //设置所有
            if (StrUtil.isNotBlank(poitype)){
                parameterMap.put("poitype",poitype);
            }
            parameterMap.put("homeorcorp",String.valueOf(homeorcorp));
            parameterMap.put("roadlevel",String.valueOf(roadlevel));
        }
        return parameterMap;
    }
    public List<MapLocation> getMapLocations() {
        return mapLocations;
    }

    public String getPoitype() {
        return poitype;
    }

    public int getRadius() {
        return radius;
    }

    public AmapExtension getExtensions() {
        return extensions;
    }

    public Boolean getBatch() {
        return batch;
    }

    public int getRoadlevel() {
        return roadlevel;
    }

    public int getHomeorcorp() {
        return homeorcorp;
    }
}
